﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

namespace ifunction.BaiduBCS.SDK
{
    /// <summary>
    /// Extension class for http operation.
    /// </summary>
    internal static partial class HttpExtension
    {
        #region Read response

        #region As Text

        /// <summary>
        /// Reads the response as text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <returns>System.String.</returns>
        public static string ReadResponseAsText(this HttpWebRequest httpWebRequest, Encoding encoding = null)
        {
            HttpStatusCode statusCode;
            return ReadResponseAsText(httpWebRequest, encoding, out statusCode);
        }

        /// <summary>
        /// Reads the response as text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="statusCode">The status code.</param>
        /// <returns>System.String.</returns>
        public static string ReadResponseAsText(this HttpWebRequest httpWebRequest, Encoding encoding, out HttpStatusCode statusCode)
        {
            WebHeaderCollection headers;
            return ReadResponseAsText(httpWebRequest, encoding, out statusCode, out headers);
        }

        /// <summary>
        /// Reads the response as text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="statusCode">The status code.</param>
        /// <param name="headers">The headers.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">ReadResponseAsText</exception>
        public static string ReadResponseAsText(this HttpWebRequest httpWebRequest, Encoding encoding, out HttpStatusCode statusCode, out WebHeaderCollection headers)
        {
            statusCode = default(HttpStatusCode);
            string result = string.Empty;
            headers = null;

            if (httpWebRequest != null)
            {
                WebResponse response = null;
                HttpWebResponse webResponse = null;

                try
                {
                    response = httpWebRequest.GetResponse();
                    result = response.ReadAsText(encoding, false);

                    webResponse = (HttpWebResponse)response;
                }
                catch (WebException webEx)
                {
                    webResponse = (HttpWebResponse)webEx.Response;
                    result = webEx.Response.ReadAsText(encoding, false);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadResponseAsText", ex);
                }
                finally
                {
                    if (webResponse != null)
                    {
                        statusCode = webResponse.StatusCode;
                        headers = webResponse.Headers;
                    }

                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Gets text content from WebResponse by specified encoding.
        /// </summary>
        /// <param name="webResponse">The WebResponse instance.</param>
        /// <param name="encoding">The encoding.</param>
        /// <returns>The content string.</returns>
        public static string ReadAsText(this WebResponse webResponse, Encoding encoding = null)
        {
            return ReadAsText(webResponse, encoding, true);
        }

        /// <summary>
        /// Reads as text.
        /// </summary>
        /// <param name="webResponse">The web response.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="closeResponse">if set to <c>true</c> [close response].</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">ReadAsText</exception>
        private static string ReadAsText(this WebResponse webResponse, Encoding encoding, bool closeResponse)
        {
            string result = string.Empty;

            if (webResponse != null)
            {
                try
                {
                    using (Stream responseStream = webResponse.GetResponseStream())
                    {
                        if (encoding == null)
                        {
                            encoding = Encoding.UTF8;
                        }

                        StreamReader streamReader = new StreamReader(responseStream, encoding, true);
                        result = streamReader.ReadToEnd();
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadAsText", ex);
                }
                finally
                {
                    if (closeResponse)
                    {
                        webResponse.Close();
                    }
                }
            }

            return result;
        }

        #endregion

        #region As GZip Text

        /// <summary>
        /// Reads the response as g zip text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <returns>System.String.</returns>
        public static string ReadResponseAsGZipText(this HttpWebRequest httpWebRequest, Encoding encoding = null)
        {
            HttpStatusCode statusCode;
            return ReadResponseAsGZipText(httpWebRequest, encoding, out  statusCode);
        }

        /// <summary>
        /// Reads the response as g zip text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="statusCode">The status code.</param>
        /// <returns>System.String.</returns>
        public static string ReadResponseAsGZipText(this HttpWebRequest httpWebRequest, Encoding encoding, out HttpStatusCode statusCode)
        {
            WebHeaderCollection header;
            return ReadResponseAsGZipText(httpWebRequest, encoding, out  statusCode, out header);
        }

        /// <summary>
        /// Reads the response as g zip text.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="statusCode">The status code.</param>
        /// <param name="headers">The headers.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">ReadResponseAsGZipText</exception>
        public static string ReadResponseAsGZipText(this HttpWebRequest httpWebRequest, Encoding encoding, out HttpStatusCode statusCode, out WebHeaderCollection headers)
        {
            statusCode = default(HttpStatusCode);
            string result = string.Empty;
            headers = null;

            if (httpWebRequest != null)
            {
                WebResponse response = null;
                HttpWebResponse webResponse = null;

                try
                {
                    response = httpWebRequest.GetResponse();
                    result = response.ReadAsGZipText(encoding, false);

                    webResponse = (HttpWebResponse)response;
                }
                catch (WebException webEx)
                {
                    webResponse = (HttpWebResponse)webEx.Response;
                    result = webEx.Response.ReadAsText(encoding, false);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadResponseAsGZipText", ex);
                }
                finally
                {
                    statusCode = webResponse.StatusCode;
                    headers = webResponse.Headers;
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Reads as g zip text.
        /// </summary>
        /// <param name="webResponse">The web response.</param>
        /// <param name="encoding">The encoding.</param>
        /// <returns>System.String.</returns>
        public static string ReadAsGZipText(this WebResponse webResponse, Encoding encoding = null)
        {
            return ReadAsGZipText(webResponse, encoding);
        }

        /// <summary>
        /// Reads as g zip text.
        /// </summary>
        /// <param name="webResponse">The web response.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="closeResponse">if set to <c>true</c> [close response].</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">ReadAsGZipText</exception>
        private static string ReadAsGZipText(this WebResponse webResponse, Encoding encoding, bool closeResponse)
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (webResponse != null)
            {
                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }

                try
                {
                    using (Stream responseStream = webResponse.GetResponseStream())
                    {
                        using (var gZipStream = new GZipStream(responseStream, CompressionMode.Decompress))
                        {
                            byte[] buffer = new byte[20480];
                            int length = gZipStream.Read(buffer, 0, 20480);
                            while (length > 0)
                            {
                                stringBuilder.Append(encoding.GetString(buffer, 0, length));
                                length = gZipStream.Read(buffer, 0, 20480);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadAsGZipText", ex);
                }
                finally
                {
                    if (closeResponse)
                    {
                        webResponse.Close();
                    }
                }
            }

            return stringBuilder.ToString();
        }

        #endregion

        #region As Bytes

        /// <summary>
        /// Reads the response as bytes.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <returns>System.Byte[].</returns>
        public static byte[] ReadResponseAsBytes(this HttpWebRequest httpWebRequest)
        {
            HttpStatusCode statusCode;
            return ReadResponseAsBytes(httpWebRequest, out statusCode);
        }

        /// <summary>
        /// Reads the response as bytes.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="statusCode">The status code.</param>
        /// <returns>System.Byte[].</returns>
        public static byte[] ReadResponseAsBytes(this HttpWebRequest httpWebRequest, out HttpStatusCode statusCode)
        {
            WebHeaderCollection header = null;
            return ReadResponseAsBytes(httpWebRequest, out statusCode, out header);
        }

        /// <summary>
        /// Reads the response as bytes.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="statusCode">The status code.</param>
        /// <param name="headers">The headers.</param>
        /// <returns>System.Byte[].</returns>
        /// <exception cref="System.InvalidOperationException">ReadResponseAsBytes</exception>
        public static byte[] ReadResponseAsBytes(this HttpWebRequest httpWebRequest, out HttpStatusCode statusCode, out WebHeaderCollection headers)
        {
            statusCode = default(HttpStatusCode);
            byte[] result = null;
            headers = null;

            if (httpWebRequest != null)
            {
                WebResponse response = null;
                HttpWebResponse webResponse = null;

                try
                {
                    response = httpWebRequest.GetResponse();
                    result = response.ReadAsBytes(false);

                    webResponse = (HttpWebResponse)response;
                }
                catch (WebException webEx)
                {
                    webResponse = (HttpWebResponse)webEx.Response;
                    result = webEx.Response.ReadAsBytes(false);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadResponseAsBytes", ex);
                }
                finally
                {
                    statusCode = webResponse.StatusCode;
                    headers = webResponse.Headers;
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }

            return result;
        }


        /// <summary>
        /// Reads as bytes.
        /// </summary>
        /// <param name="webResponse">The web response.</param>
        /// <returns>System.Byte[].</returns>
        private static byte[] ReadAsBytes(this WebResponse webResponse)
        {
            return ReadAsBytes(webResponse, true);
        }

        /// <summary>
        /// Reads as bytes.
        /// </summary>
        /// <param name="webResponse">The web response.</param>
        /// <param name="closeResponse">if set to <c>true</c> [close response].</param>
        /// <returns>System.Byte[].</returns>
        /// <exception cref="System.InvalidOperationException">ReadAsBytes</exception>
        private static byte[] ReadAsBytes(this WebResponse webResponse, bool closeResponse)
        {
            byte[] result = null;

            if (webResponse != null)
            {
                try
                {
                    using (Stream responseStream = webResponse.GetResponseStream())
                    {
                        result = responseStream.ToBytes();
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ReadAsBytes", ex);
                }
                finally
                {
                    if (closeResponse)
                    {
                        webResponse.Close();
                    }
                }
            }

            return result;
        }

        #endregion

        #endregion

        #region Fill Data On HttpWebRequest

        /// <summary>
        /// Fills the file data.
        /// Reference: http://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="postData">The post data.</param>
        /// <param name="fileCollection">The file collection.
        /// Key: file name. e.g.: sample.txt
        /// Value: file data in byte array.</param>
        /// <param name="paramName">Name of the parameter.</param>
        /// <exception cref="System.InvalidOperationException">FillFileData</exception>
        public static void FillFileData(this HttpWebRequest httpWebRequest, NameValueCollection postData, Dictionary<string, byte[]> fileCollection, string paramName)
        {
            try
            {
                string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

                httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
                httpWebRequest.Method = "POST";
                httpWebRequest.KeepAlive = true;
                httpWebRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;

                using (var stream = new MemoryStream())
                {

                    byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

                    string formDataTemplate = "\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";

                    if (postData != null)
                    {
                        foreach (string key in postData.Keys)
                        {
                            string formItem = string.Format(formDataTemplate, key, postData[key]);
                            byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem);
                            stream.Write(formItemBytes, 0, formItemBytes.Length);
                        }
                    }

                    stream.Write(boundaryBytes, 0, boundaryBytes.Length);

                    string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n";

                    if (fileCollection != null)
                    {
                        foreach (var key in fileCollection.Keys)
                        {
                            string header = string.Format(headerTemplate, paramName, key);
                            byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
                            stream.Write(headerBytes, 0, headerBytes.Length);

                            stream.Write(fileCollection[key], 0, fileCollection[key].Length);

                            stream.Write(boundaryBytes, 0, boundaryBytes.Length);
                        }
                    }

                    httpWebRequest.ContentLength = stream.Length;
                    stream.Position = 0;
                    byte[] tempBuffer = new byte[stream.Length];
                    stream.Read(tempBuffer, 0, tempBuffer.Length);

                    using (Stream requestStream = httpWebRequest.GetRequestStream())
                    {
                        requestStream.Write(tempBuffer, 0, tempBuffer.Length);
                        requestStream.Flush();
                        requestStream.Close();
                    }

                    stream.Close();
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("FillFileData", ex);
            }
        }

        /// <summary>
        /// Fills the file data on HTTP web request.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="postData">The post data.</param>
        /// <param name="fileFullName">Full name of the file.</param>
        /// <param name="paramName">Name of the param.</param>
        /// <exception cref="System.InvalidOperationException">FillFileData</exception>
        public static void FillFileData(this HttpWebRequest httpWebRequest, NameValueCollection postData, string fileFullName, string paramName)
        {
            try
            {
                byte[] fileData = File.ReadAllBytes(fileFullName);
                var fileName = Path.GetFileName(fileFullName);

                Dictionary<string, byte[]> fileCollection = new Dictionary<string, byte[]>();
                fileCollection.Add(fileName, fileData);

                FillFileData(httpWebRequest, postData, fileCollection, paramName);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("FillFileData", ex);
            }
        }

        /// <summary>
        /// Fills post data on HttpWebRequest.
        /// </summary>
        /// <param name="httpWebRequest">The HttpWebRequest instance.</param>
        /// <param name="method">The method.</param>
        /// <param name="dataMappings">The data mappings.</param>
        /// <param name="encoding">The encoding.</param>
        public static void FillData(this HttpWebRequest httpWebRequest, string method, Dictionary<string, string> dataMappings, Encoding encoding = null)
        {
            if (httpWebRequest != null)
            {
                if (encoding == null)
                {
                    encoding = Encoding.ASCII;
                }

                StringBuilder sb = new StringBuilder();
                if (dataMappings != null)
                {
                    foreach (string key in dataMappings.Keys)
                    {
                        string value = string.Empty;
                        if (dataMappings[key] != null)
                        {
                            value = dataMappings[key];
                        }
                        sb.Append(key + "=" + value.Trim() + "&");
                    }

                }
                if (sb.Length > 0)
                {
                    sb.Remove(sb.Length - 1, 1);
                }

                byte[] data = encoding.GetBytes(sb.ToString());

                httpWebRequest.Method = method;
                httpWebRequest.ContentType = "application/x-www-form-urlencoded";
                httpWebRequest.ContentLength = data.Length;
                Stream stream = httpWebRequest.GetRequestStream();
                stream.Write(data, 0, data.Length);
                stream.Close();
            }
        }

        /// <summary>
        /// Fills the data on HTTP web request.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="method">The method.</param>
        /// <param name="data">The data.</param>
        public static void FillData(this HttpWebRequest httpWebRequest, string method, byte[] data)
        {
            if (httpWebRequest != null && data != null)
            {
                httpWebRequest.Method = method;
                httpWebRequest.ContentType = "text/xml; charset=utf-8";
                httpWebRequest.ContentLength = data.Length;
                Stream dataStream = httpWebRequest.GetRequestStream();
                dataStream.Write(data, 0, data.Length);
                dataStream.Close();
            }
        }

        /// <summary>
        /// Fills the data on HTTP web request.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="method">The method.</param>
        /// <param name="data">The data.</param>
        public static void FillData(this HttpWebRequest httpWebRequest, string method, string data)
        {
            FillData(httpWebRequest, method, data, null);
        }

        /// <summary>
        /// Fills the data on HTTP web request.
        /// </summary>
        /// <param name="httpWebRequest">The HTTP web request.</param>
        /// <param name="method">The method.</param>
        /// <param name="data">The data.</param>
        /// <param name="encodingToByte">The encoding to byte.</param>
        public static void FillData(this HttpWebRequest httpWebRequest, string method, string data, Encoding encodingToByte)
        {
            byte[] byteArray = null;

            if (!string.IsNullOrWhiteSpace(data))
            {
                if (encodingToByte == null)
                {
                    encodingToByte = Encoding.UTF8;
                }
                byteArray = encodingToByte.GetBytes(data);
            }

            FillData(httpWebRequest, method, byteArray);
        }

        #endregion

        /// <summary>
        /// Creates the HTTP web request.
        /// </summary>
        /// <param name="anyObject">Any object.</param>
        /// <param name="methodType">Type of the method.</param>
        /// <param name="targetUrl">The target URL.</param>
        /// <param name="referer">The referer.</param>
        /// <param name="userAgent">The user agent.</param>
        /// <param name="cookieContainer">The cookie container.</param>
        /// <param name="cookieString">The cookie string.</param>
        /// <param name="accept">The accept.</param>
        /// <returns>HttpWebRequest.</returns>
        public static HttpWebRequest CreateHttpWebRequest(this object anyObject, string methodType, string targetUrl, string referer, string userAgent, CookieContainer cookieContainer = null, string cookieString = null, string accept = null)
        {
            HttpWebRequest httpWebRequest = null;
            try
            {
                httpWebRequest = (HttpWebRequest)WebRequest.Create(targetUrl);

                if (string.IsNullOrWhiteSpace(accept))
                {
                    accept = "*/*";
                }
                httpWebRequest.Accept = accept;

                if (!string.IsNullOrWhiteSpace(referer))
                {
                    httpWebRequest.Referer = referer;
                }

                if (!string.IsNullOrWhiteSpace(userAgent))
                {
                    httpWebRequest.UserAgent = userAgent;
                }

                httpWebRequest.CookieContainer = cookieContainer == null ? new CookieContainer() : cookieContainer;
                if (!string.IsNullOrWhiteSpace(cookieString))
                {
                    CookieCollection collection = new CookieCollection();
                    collection.SetCookieByString(cookieString, httpWebRequest.RequestUri.Host);

                    httpWebRequest.CookieContainer.Add(httpWebRequest.RequestUri, collection);
                }

                httpWebRequest.Method = string.IsNullOrWhiteSpace(methodType) ? "GET" : methodType;
            }
            catch { }

            return httpWebRequest;
        }

        /// <summary>
        /// Creates the HTTP web request.
        /// </summary>
        /// <param name="anyObject">Any object.</param>
        /// <param name="targetUrl">The target URL.</param>
        /// <param name="method">The method.</param>
        /// <returns>HttpWebRequest.</returns>
        public static HttpWebRequest CreateHttpWebRequest(this object anyObject, string targetUrl, string method = "GET")
        {
            return CreateHttpWebRequest(anyObject, method, targetUrl, null, null);
        }

        /// <summary>
        /// Creates the HTTP web request.
        /// </summary>
        /// <param name="uriObject">The URI object.</param>
        /// <param name="method">The method.</param>
        /// <returns>HttpWebRequest.</returns>
        public static HttpWebRequest CreateHttpWebRequest(this Uri uriObject, string method = "GET")
        {
            if (uriObject != null)
            {
                return CreateHttpWebRequest(uriObject.ToString(), method);
            }

            return null;
        }

        /// <summary>
        /// Sets the cookie by string.
        /// </summary>
        /// <param name="cookieCollection">The cookie collection.</param>
        /// <param name="cookieString">The cookie string.</param>
        /// <param name="hostDomain">The host domain.</param>
        public static void SetCookieByString(this CookieCollection cookieCollection, string cookieString, string hostDomain)
        {
            if (cookieCollection != null && !string.IsNullOrWhiteSpace(cookieString))
            {
                string[] cookieProperties = cookieString.Split(new char[] { ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var property in cookieProperties)
                {
                    string[] keyValue = property.Split(new char[] { '=' }, 2);
                    if (keyValue.Length >= 2)
                    {
                        string key = keyValue[0];
                        string value = keyValue[1];
                        Cookie cookie = new Cookie(key, value, null, hostDomain);

                        cookieCollection.Add(cookie);
                    }
                }
            }
        }

        /// <summary>
        /// Sets the cookie by string.
        /// </summary>
        /// <param name="cookieContainer">The cookie container.</param>
        /// <param name="cookieString">The cookie string.</param>
        /// <param name="hostDomain">The host domain.</param>
        public static void SetCookieByString(this CookieContainer cookieContainer, string cookieString, Uri hostDomain)
        {
            if (hostDomain != null && cookieContainer != null && !string.IsNullOrWhiteSpace(cookieString))
            {
                var cookieCollection = cookieContainer.GetCookies(hostDomain);

                if (cookieCollection == null)
                {
                    cookieCollection = new CookieCollection();
                    cookieCollection.SetCookieByString(cookieString, hostDomain.Host);
                    cookieContainer.Add(cookieCollection);
                }
            }
        }

        /// <summary>
        /// Automatics the cookie raw string.
        /// </summary>
        /// <param name="cookieCollection">The cookie collection.</param>
        /// <returns>System.String.</returns>
        public static string ToCookieRawString(this CookieCollection cookieCollection)
        {
            StringBuilder builder = new StringBuilder();

            if (cookieCollection != null)
            {
                foreach (Cookie cookie in cookieCollection)
                {
                    builder.AppendFormat("{0}={1}; ", cookie.Name, cookie.Value);
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// To the key value string.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <param name="needUrlEncode">if set to <c>true</c> [need URL encode].</param>
        /// <returns>System.String.</returns>
        public static string ToKeyValueString(this Dictionary<string, string> parameters, bool needUrlEncode = true)
        {
            StringBuilder builder = new StringBuilder();

            if (parameters != null && parameters.Count > 0)
            {
                foreach (var one in parameters)
                {
                    builder.AppendFormat("{0}={1}&", one.Key, needUrlEncode ? one.Value.ToUrlEncodedText() : one.Value);
                }
            }

            return builder.ToString().TrimEnd('&');
        }

        /// <summary>
        /// Parses to key value pair collection.
        /// </summary>
        /// <param name="keyValuePairString">The key value pair string.</param>
        /// <returns>WebDictionary.</returns>
        public static NameValueCollection ParseToKeyValuePairCollection(this string keyValuePairString)
        {
            NameValueCollection result = new NameValueCollection();

            if (!string.IsNullOrWhiteSpace(keyValuePairString))
            {
                string[] pairs = keyValuePairString.Split('&');
                foreach (var one in pairs)
                {
                    if (!string.IsNullOrWhiteSpace(one))
                    {
                        string[] keyValuePair = one.Split('=');

                        if (keyValuePair.Length == 2)
                        {
                            string key = keyValuePair[0];
                            string value = keyValuePair[1];

                            if (!string.IsNullOrWhiteSpace(key))
                            {
                                result.Set(key, value.GetStringValue());
                            }
                        }
                    }
                }
            }

            return result;
        }

        #region Http Async

        /// <summary>
        /// Class AsyncHttpState.
        /// </summary>
        private class AsyncHttpState
        {
            /// <summary>
            /// All done
            /// </summary>
            protected ManualResetEvent allDone = new ManualResetEvent(false);

            /// <summary>
            /// The response callback
            /// </summary>
            protected Action<WebResponse> responseCallback = null;

            /// <summary>
            /// Gets or sets the timeout.
            /// </summary>
            /// <value>The timeout.</value>
            public int Timeout
            {
                get;
                protected set;
            }

            /// <summary>
            /// Gets the request URI.
            /// </summary>
            /// <value>The request URI.</value>
            public Uri RequestUri
            {
                get
                {
                    return this.Request != null ? this.Request.RequestUri : null;
                }
            }

            /// <summary>
            /// Gets the request URL.
            /// </summary>
            /// <value>The request URL.</value>
            public string RequestUrl
            {
                get
                {
                    return this.RequestUri != null ? this.RequestUri.ToString() : string.Empty;
                }
            }

            /// <summary>
            /// Gets or sets the request.
            /// </summary>
            /// <value>The request.</value>
            public HttpWebRequest Request
            {
                get;
                protected set;
            }

            /// <summary>
            /// Gets or sets the response.
            /// </summary>
            /// <value>The response.</value>
            public WebResponse Response
            {
                get;
                protected set;
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="AsyncHttpState" /> class.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <param name="timeout">The timeout.</param>
            public AsyncHttpState(HttpWebRequest request, int timeout = 120000)
            {
                this.Request = request;

                this.Timeout = timeout > 0 ? timeout : 120000;
            }

            /// <summary>
            /// Gets the response asynchronous.
            /// </summary>
            /// <param name="callback">The callback.</param>
            /// <exception cref="System.ArgumentNullException">callback</exception>
            /// <exception cref="System.InvalidOperationException">AsyncGetResponse</exception>
            public void GetResponseAsync(Action<WebResponse> callback)
            {
                try
                {
                    if (responseCallback != null)
                    {
                        throw new ArgumentNullException("callback");
                    }

                    this.responseCallback = callback;

                    IAsyncResult result = (IAsyncResult)this.Request.BeginGetResponse(new AsyncCallback(ResponseCallback), this);

                    ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), this.Request, this.Timeout, true);

                    // The response came in the allowed time. The work processing will happen in the 
                    // callback function.
                    allDone.WaitOne();

                    // Release the HttpWebResponse resource.
                    this.Response.Close();
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("AsyncGetResponse", ex);
                }
            }

            /// <summary>
            /// Timeouts the callback.
            /// </summary>
            /// <param name="state">The state.</param>
            /// <param name="timedOut">if set to <c>true</c> [timed out].</param>
            private static void TimeoutCallback(object state, bool timedOut)
            {
                if (timedOut)
                {
                    HttpWebRequest request = state as HttpWebRequest;
                    if (request != null)
                    {
                        request.Abort();
                    }
                }
            }

            /// <summary>
            /// Responses the callback.
            /// </summary>
            /// <param name="asynchronousResult">The asynchronous result.</param>
            /// <exception cref="System.InvalidOperationException">ResponseCallback</exception>
            /// <exception cref="InvalidOperationException">ResponseCallback</exception>
            private static void ResponseCallback(IAsyncResult asynchronousResult)
            {
                AsyncHttpState asyncHttpState = (AsyncHttpState)asynchronousResult.AsyncState;

                try
                {
                    HttpWebRequest myHttpWebRequest = asyncHttpState.Request;

                    asyncHttpState.Response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

                    if (asyncHttpState.responseCallback != null)
                    {
                        asyncHttpState.responseCallback(asyncHttpState.Response);
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("ResponseCallback", ex);
                }
                finally
                {
                    asyncHttpState.allDone.Set();
                }
            }
        }

        /// <summary>
        /// Proceeds the response asynchronous.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="callback">The callback.</param>
        public static void ProceedResponseAsync(this HttpWebRequest request, Action<WebResponse> callback = null)
        {
            if (request != null)
            {
                AsyncHttpState state = new AsyncHttpState(request);
                state.GetResponseAsync(callback);
            }
        }


        #endregion
    }
}
